package edu.umassd.raddacl.dataset;

import java.util.Iterator;

import javax.annotation.PostConstruct;

import org.apache.commons.math3.linear.BlockRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import edu.umassd.raddacl.Cluster;
import edu.umassd.raddacl.DataSet;
import edu.umassd.raddacl.Observation;
import edu.umassd.raddacl.calc.LoopAndCompareAlgorithm;
import edu.umassd.raddacl.parsing.DataSetLoader;

/**
 * A dataset represented as a matrix of data.
 * 
 * @author Dan Avila
 * 
 */
@Component
public class DefaultDataSet implements DataSet
{
	@Autowired
	private DataSetLoader loader;

	@Autowired
	private DefaultFactory fac;

	private RealMatrix data;

	/**
	 * Ensures that this dataset is populated immediately following creation.
	 */
	@PostConstruct
	protected void loadData()
	{
		loader.load();
	}

	@Override
	public Iterator<Observation> iterator()
	{
		return new ObservationIterator();
	}

	@Override
	public Observation getObservation(int index)
	{
		return DefaultObservation.createNewObservation(index, this);
	}

	@Override
	public int getNumberOfObservations()
	{
		return data.getRowDimension();
	}

	@Override
	public int getNumberOfAttributes()
	{
		return data.getColumnDimension();
	}

	@Override
	public void loop(LoopAndCompareAlgorithm loop)
	{
		for (int i = 0; i < getNumberOfObservations() - 1; i++)
		{
			for (int x = i + 1; x < getNumberOfObservations(); x++)
			{
				Observation obs1 = getObservation(i);
				Observation obs2 = getObservation(x);

				loop.loop(obs1, obs2);
			}
		}
	}

	/**
	 * Navigates through observations.
	 * 
	 * @author Dan Avila
	 * 
	 */
	private class ObservationIterator implements Iterator<Observation>
	{
		private int value = 0;

		@Override
		public boolean hasNext()
		{
			return value < getNumberOfObservations();
		}

		@Override
		public Observation next()
		{
			try
			{
				return DefaultObservation.createNewObservation(value,
				        DefaultDataSet.this);
			}
			finally
			{
				value++;
			}
		}

		@Override
		public void remove()
		{
			throw new UnsupportedOperationException(
			        "Cannot remove an observation from a dataset.");
		}
	}

	@Override
	public void set(double[][] data)
	{
		this.data = new BlockRealMatrix(data);
	}

	/**
	 * Gets an attributes value for a particular observation.
	 * 
	 * @param obs
	 *            - the index of the observation in the dataset.
	 * @param attr
	 *            - the index of the attribute in an observation.
	 * @return the attribute's value.
	 */
	Double get(int obs, int attr)
	{
		return Double.valueOf(data.getEntry(obs, attr));
	}

	@Override
	public Cluster toCluster()
	{
		return fac.createCluster(this);
	}
}
